package sm.upgrade.api.impl;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.HibernateTemplate;

import sm.dam.api.DBDictService;
import sm.dbdict.DBDictUtil;
import sm.dbdict.def.DBDict;
import sm.dbdict.def.Field;
import sm.dbdict.def.Key;
import sm.hibernate.DAMUtil;
import sm.upgrade.api.BatchDataUpgradeService;
import sm.upgrade.api.DataService;
import sm.upgrade.api.DataUpgradeInterceptor;
import sm.upgrade.api.DataUpgradeService;
import sm.upgrade.exception.UpgradeException;
import sm.upgrade.util.DataUpgradePolicy;
import sm.upgrade.util.KeyValueChangeManager;
import sm.upgrade.util.KeyValueChangePolicy;
import sm.util.SMMap;
import sm.util.Util;

public class DataUpgradeServiceImpl implements DataUpgradeService {

	private static Logger logger = LoggerFactory
			.getLogger(DataUpgradeService.class);

	private HibernateTemplate template;

	private DataUpgradePolicy dataUpgradePolicy;

	private KeyValueChangeManager keyValueChangeManager;

	private BatchDataUpgradeService batchDataUpgradeService;

	private int batchSize = 50;

	public void setBatchDataUpgradeService(
			BatchDataUpgradeService batchDataUpgradeService) {
		this.batchDataUpgradeService = batchDataUpgradeService;
	}

	public void setBatchSize(int batchSize) {
		this.batchSize = batchSize;
	}

	public void setKeyValueChangeManager(
			KeyValueChangeManager keyValueChangeManager) {
		this.keyValueChangeManager = keyValueChangeManager;
	}

	public void setTemplate(HibernateTemplate template) {
		this.template = template;
	}

	public void setDataUpgradePolicy(DataUpgradePolicy dataUpgradePolicy) {
		this.dataUpgradePolicy = dataUpgradePolicy;
	}

	@Override
	public void upgradeData(String fileName, DataService upgradeDataService,
			DataService baseDataService) throws UpgradeException {
		DBDict upgradeDBDict = upgradeDataService.getDBDict(fileName);
		if (upgradeDBDict == null) {
			return;
		}

		if (logger.isDebugEnabled()) {
			logger.debug("Upgrade data for " + fileName);
		}
		String entityName = Util.radEntityNameToJava(fileName);

		// get identifier for upgrade
		Key identifier = DBDictUtil.getIdentifier(upgradeDBDict);
		List<Field> keyFields = DBDictUtil.getKeyFields(identifier,
				upgradeDBDict.getField());
		String[] radKeyPaths = DBDictUtil.getKeyPaths(keyFields);
		String[] keyPaths = Util.radFieldPathsToJava(radKeyPaths);
		String[] joinedArrayPaths = DAMUtil.getJoinedArrayPaths(upgradeDBDict);

		// get identifier for base
		String[] baseRadKeyPaths = radKeyPaths;
		String[] baseKeyPaths = keyPaths;
		String[] baseJoinedArrayPaths = joinedArrayPaths;
		boolean isBaseQueryable = false;
		boolean useBaseKey = false;
		String[] oldRadKeyPaths = dataUpgradePolicy.getBaseIdentifier(fileName);
		if (baseDataService != null) {
			// check if base is queryable
			DBDict baseDBDict = baseDataService.getDBDict(fileName);
			if (baseDBDict != null) {
				if (oldRadKeyPaths != null) {
					baseRadKeyPaths = oldRadKeyPaths;
					baseKeyPaths = Util.radFieldPathsToJava(oldRadKeyPaths);
					useBaseKey = true;
				}
				isBaseQueryable = isQueryable(baseDBDict, baseRadKeyPaths);
				if (isBaseQueryable) {
					baseJoinedArrayPaths = DAMUtil
							.getJoinedArrayPaths(baseDBDict);
				}
			}
		}

		// get key value change policy
		KeyValueChangePolicy keyValueChangePolicy = keyValueChangeManager
				.getKeyValueChangePolicy(fileName);

		// get data upgrade interceptor
		DataUpgradeInterceptor interceptor = dataUpgradePolicy
				.getInterceptor(fileName);

		// get field to be compared
		List<String> fieldPaths = getFieldValuePaths(upgradeDBDict);
		// skip no pk keys object
		List<String> skippedList = new ArrayList<String>();

		int firstResult = 0;
		List<SMMap> upgradeList = upgradeDataService.getNextBatchDatas(
				entityName, firstResult, batchSize, joinedArrayPaths);
		while (upgradeList != null && upgradeList.size() > 0) {
			batchDataUpgradeService.upgradeData(fileName, upgradeList,
					fieldPaths, skippedList, keyValueChangePolicy, interceptor,
					upgradeDataService, keyPaths, joinedArrayPaths,
					isBaseQueryable, baseDataService, useBaseKey, baseKeyPaths,
					baseJoinedArrayPaths);

			// fetch next batch
			firstResult += batchSize;
			upgradeList = upgradeDataService.getNextBatchDatas(entityName,
					firstResult, batchSize, joinedArrayPaths);
		}
	}

	@Override
	public void showConflict(String fileName, DataService upgradeDataService,
			DataService baseDataService) {
		DBDict upgradeDBDict = upgradeDataService.getDBDict(fileName);
		SMMap customerDBDictMap = (SMMap) template.get(DBDictService.DBDICT,
				fileName);
		// skip unmatched dbdict
		if (upgradeDBDict != null && customerDBDictMap != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Check data conflict for " + fileName);
			}

			String entityName = Util.radEntityNameToJava(fileName);

			// get identifier for upgrade
			Key identifier = DBDictUtil.getIdentifier(upgradeDBDict);
			List<Field> keyFields = DBDictUtil.getKeyFields(identifier,
					upgradeDBDict.getField());
			String[] radKeyPaths = DBDictUtil.getKeyPaths(keyFields);
			String[] keyPaths = Util.radFieldPathsToJava(radKeyPaths);
			String[] joinedArrayPaths = DAMUtil
					.getJoinedArrayPaths(upgradeDBDict);

			// get identifier for base
			String[] baseRadKeyPaths = radKeyPaths;
			String[] baseKeyPaths = keyPaths;
			String[] baseJoinedArrayPaths = joinedArrayPaths;
			boolean isBaseQueryable = false;
			boolean useBaseKey = false;
			String[] oldRadKeyPaths = dataUpgradePolicy
					.getBaseIdentifier(fileName);
			if (baseDataService != null) {
				// check if base is queryable
				DBDict baseDBDict = baseDataService.getDBDict(fileName);
				if (baseDBDict != null) {
					if (oldRadKeyPaths != null) {
						baseRadKeyPaths = oldRadKeyPaths;
						baseKeyPaths = Util.radFieldPathsToJava(oldRadKeyPaths);
						useBaseKey = true;
					}

					isBaseQueryable = isQueryable(baseDBDict, baseRadKeyPaths);
					if (isBaseQueryable) {
						baseJoinedArrayPaths = DAMUtil
								.getJoinedArrayPaths(baseDBDict);
					}
				}
			}

			// get key value change policy
			KeyValueChangePolicy keyValueChangePolicy = keyValueChangeManager
					.getKeyValueChangePolicy(fileName);

			// get field to be compared
			List<String> fieldPaths = getFieldValuePaths(upgradeDBDict);
			// skip no pk keys object
			List<String> skippedList = new ArrayList<String>();

			int firstResult = 0;
			List<SMMap> upgradeList = upgradeDataService.getNextBatchDatas(
					entityName, firstResult, batchSize, joinedArrayPaths);
			while (upgradeList != null && upgradeList.size() > 0) {
				batchDataUpgradeService.showConflict(fileName, upgradeList,
						fieldPaths, skippedList, keyValueChangePolicy,
						upgradeDataService, keyPaths, joinedArrayPaths,
						isBaseQueryable, baseDataService, useBaseKey,
						baseKeyPaths, baseJoinedArrayPaths);

				// fetch next batch
				firstResult += batchSize;
				upgradeList = upgradeDataService.getNextBatchDatas(entityName,
						firstResult, batchSize, joinedArrayPaths);
			}

		}
	}

	private List<String> getFieldValuePaths(DBDict dbdict) {
		List<String> fieldPaths = DAMUtil.getFieldValuePath(dbdict);
		fieldPaths
				.removeAll(dataUpgradePolicy.getIgnoreFields(dbdict.getName()));
		return fieldPaths;
	}

	private boolean isQueryable(DBDict dbdict, String[] fieldPaths) {
		boolean isQueryable = true;
		for (String radKeyPath : fieldPaths) {
			Field baseField = DBDictUtil
					.getField(radKeyPath, dbdict.getField());
			if (!DBDictUtil.isDBColumn(baseField)) {
				isQueryable = false;
				break;
			}
		}
		return isQueryable;
	}
}
